package com.hsh.maoduo.util;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.poi.PoiChecker;
import cn.hutool.poi.excel.ExcelWriter;
import com.hsh.maoduo.anno.SheetInfo;
import lombok.NoArgsConstructor;

import java.io.File;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * @Author hsh
 * @DateTime 2023/11/23 13:53
 **/
@NoArgsConstructor
public class MyExcelWriter extends ExcelWriter {


    /**
     * 写出数据，本方法只是将数据写入Workbook中的Sheet，并不写出到文件<br>
     * 写出的起始行为当前行号，可使用{@link #getCurrentRow()}方法调用，根据写出的的行数，当前行号自动增加<br>
     * 样式为默认样式，可使用{@link #getCellStyle()}方法调用后自定义默认样式<br>
     * data中元素支持的类型有：
     *
     * <p>
     * 1. Iterable，既元素为一个集合，元素被当作一行，data表示多行<br>
     * 2. Map，既元素为一个Map，第一个Map的keys作为首行，剩下的行为Map的values，data表示多行 <br>
     * 3. Bean，既元素为一个Bean，第一个Bean的字段名列表会作为首行，剩下的行为Bean的字段值列表，data表示多行 <br>
     * 4. 无法识别，不输出
     * </p>
     *
     * @param data 数据
     * @return this
     */
    public ExcelWriter write(Iterable<?> data) {
        int index = 0;
        for (Object object : data) {
            if (object instanceof Iterable) {
                // 普通多行数据
                writeRow((Iterable<?>) object);
            } else if (object instanceof Map) {
                // Map表示一行，第一条数据的key做为标题行
                writeRows((Map<?, ?>) object, 0 == index);
            } else if (BeanUtil.isBean(object.getClass())) {
                // 一个Bean对象表示一行,ExcelWriter中使用的 BeanUtil.beanToMap(object) ,返回结果使用HashMap接收，导致写入excel时列的顺序按Hash值排序
                //this.writeRows(BeanUtil.beanToMap(object), 0 == index);
                // 使用自定义的beanToMap(),按照读取顺序排序，实现按字段顺序写入excel中的列
                writeRows(beanToMap(object), 0 == index);
            } else {
                break;
            }
            index++;
        }
        if (0 == index) {
            // 在无法识别元素类型的情况下，做为一行对待
            writeRow(data);
        }
        return this;
    }

    /**
     * 自定义beanToMap(),实现按字段顺序写入excel中的列
     * @param obj
     * @return 使用LinkedHashMap，按照读取顺序排序
     */
    public Map<String, Object> beanToMap(Object obj) {
        Map map = new LinkedHashMap<String, String>();
        Class clazz = obj.getClass();
        //设置sheet名称
        SheetInfo anno = (SheetInfo) clazz.getAnnotation(SheetInfo.class);
        if (anno != null) {
            super.setOrCreateSheet(anno.name());
        }
        List<Field> fields = Arrays.asList(ReflectUtil.getFields(clazz));
        for (Field field : fields) {
            map.put(field.getName(), ReflectUtil.getFieldValue(obj, field));
        }
        return map;
    }

    /**
     * 获得{@link ExcelWriter}
     *
     * @param destFile  目标文件
     * @param sheetName sheet表名
     * @return {@link ExcelWriter}
     */
    public static MyExcelWriter getWriter(File destFile, String sheetName) {
        try {
            return new MyExcelWriter(destFile, sheetName);
        } catch (NoClassDefFoundError e) {
            throw PoiChecker.transError(e);
        }
    }

    public MyExcelWriter(File destFile, String sheetName) {
        super(destFile, sheetName);
    }
}
